
use crate::http::{HttpContentType, HttpResponse, IntoBytes};

pub trait IntoResponse where Self: Sized {
    fn status_code(&self) -> u16;
    fn reason_phrase(&self) -> String {
        HttpResponse::default_reason(self.status_code()).to_string()
    }
    fn content_type(&self) -> HttpContentType;
    fn into_body(self) -> Vec<u8>;

    fn into_response(self) -> HttpResponse {
        HttpResponse::builder()
            .status(self.status_code())
            .reason(self.reason_phrase())
            .content_type(self.content_type())
            .body_bytes(self.into_body())
            .build()
            .unwrap()
    }
}

impl IntoResponse for HttpResponse {
    fn content_type(&self) -> HttpContentType {
        self.headers.content_type.unwrap()
    }

    fn into_body(self) -> Vec<u8> {
        self.body.map(|body| body.to_bytes()).unwrap_or(vec![])
    }

    fn status_code(&self) -> u16 {
        self.line.status_code
    }

    fn into_response(self) -> HttpResponse {
        self
    }
}

impl IntoResponse for String {
    fn content_type(&self) -> HttpContentType {
        HttpContentType::TextPlain
    }

    fn status_code(&self) -> u16 {
        200
    }

    fn into_body(self) -> Vec<u8> {
        self.into_bytes()
    }
}

macro_rules! impl_into_response_for_numeric {
    ($($t:ty),*) => {
        $(
            impl IntoResponse for $t {
                fn content_type(&self) -> HttpContentType {
                    HttpContentType::TextPlain
                }

                fn status_code(&self) -> u16 {
                    200
                }

                fn into_body(self) -> Vec<u8> {
                    self.to_string().into_bytes()
                }
            }
        )*
    };
}

impl_into_response_for_numeric!(i8, u8, i16, u16, i32, u32, i64, u64, isize, usize, f32, f64);